/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.ui.internal;

import java.util.ArrayList;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.INavigationLocation;
import org.eclipse.ui.INavigationLocationProvider;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.XMLMemento;

/*
 * Wraps the INavigationLocation and keeps editor info.
 */
public class NavigationHistoryEntry {

	private IWorkbenchPage page;

	NavigationHistoryEditorInfo editorInfo;

	String historyText;

	/* Both may be set at the same time. */
	INavigationLocation location;

	private IMemento locationMemento;

	/**
	 * Constructs a new HistoryEntry and intializes its editor input and editor id.
	 */
	public NavigationHistoryEntry(NavigationHistoryEditorInfo editorInfo, IWorkbenchPage page, IEditorPart part,
			INavigationLocation location) {
		this.editorInfo = editorInfo;
		this.page = page;
		this.location = location;
		if (location != null) {
			historyText = location.getText();
		}
		// ensure that the historyText is initialized to something
		if (historyText == null || historyText.isEmpty()) {
			if (part != null) {
				historyText = part.getTitle();
			}
		}
	}

	/**
	 * Restores the state of the entry and the location if needed and then restores
	 * the location.
	 */
	void restoreLocation() {
		if (editorInfo.editorInput != null && editorInfo.editorID != null) {
			try {
				IEditorPart editor = page.openEditor(editorInfo.editorInput, editorInfo.editorID, true);
				if (location == null) {
					if (editor instanceof INavigationLocationProvider) {
						location = ((INavigationLocationProvider) editor).createEmptyNavigationLocation();
					}
				}

				if (location != null) {
					if (locationMemento != null) {
						location.setInput(editorInfo.editorInput);
						location.restoreState(locationMemento);
						locationMemento = null;
					}
					location.restoreLocation();
				}
			} catch (PartInitException e) {
				// ignore for now
			}
		}
	}

	/**
	 * Return the label to display in the history drop down list. Use the history
	 * entry text if the location has not been restored yet.
	 */
	String getHistoryText() {
		if (location != null) {
			// location exists or has been restored, use its text.
			// Also update the historyText so that this value will
			// be saved. Doing so handles cases where getText() value
			// may be dynamic.
			String text = location.getText();
			if ((text == null) || text.isEmpty()) {
				text = historyText;
			} else {
				historyText = text;
			}
			return text;
		}
		return historyText;
	}

	/**
	 * Saves the state of this entry and its location. Returns true if possible
	 * otherwise returns false.
	 */
	boolean handlePartClosed() {
		if (!editorInfo.isPersistable()) {
			return false;
		}
		if (location != null) {
			locationMemento = XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_POSITION);
			location.saveState(locationMemento);
			location.releaseState();
		}
		return true;
	}

	/**
	 * Saves the state of this entry and its location.
	 */
	void saveState(IMemento mem, ArrayList<NavigationHistoryEntry> entries) {
		mem.putString(IWorkbenchConstants.TAG_HISTORY_LABEL, getHistoryText());
		if (locationMemento != null) {
			IMemento childMem = mem.createChild(IWorkbenchConstants.TAG_POSITION);
			childMem.putMemento(locationMemento);
		} else if (location != null) {
			IMemento childMem = mem.createChild(IWorkbenchConstants.TAG_POSITION);
			location.saveState(childMem);
		}
	}

	/**
	 * Restore the state of this entry.
	 */
	void restoreState(IMemento mem) {
		historyText = mem.getString(IWorkbenchConstants.TAG_HISTORY_LABEL);
		locationMemento = mem.getChild(IWorkbenchConstants.TAG_POSITION);
	}

	@Override
	public String toString() {
		return "Input<" + editorInfo.editorInput + "> Details<" + location + ">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}

	/**
	 * Disposes this entry and its location.
	 */
	void dispose() {
		if (location != null) {
			location.dispose();
		}
		editorInfo = null;
	}

	/**
	 * Merges this entry into the current entry. Returns true if the merge was
	 * possible otherwise returns false.
	 */
	boolean mergeInto(NavigationHistoryEntry currentEntry) {
		if (editorInfo.editorInput != null && editorInfo.editorInput.equals(currentEntry.editorInfo.editorInput)) {
			if (location != null) {
				if (currentEntry.location == null) {
					currentEntry.location = location;
					location = null;
					return true;
				}
				return location.mergeInto(currentEntry.location);
			} else if (currentEntry.location == null) {
				return true;
			}
		}
		return false;
	}
}
